package bancosys.tec.utils.db.converter;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;

import jmine.tec.utils.UtilsMessages;
import bancosys.tec.utils.db.CouldNotProcessStatementException;
import bancosys.tec.utils.db.StatementSplitter;

/**
 * @author Marcio Ribeiro (mmr)
 * @created Apr 25, 2007
 */
public abstract class StatementDbConverter extends DbConverter {

    /**
     * C'tor
     * 
     * @param inputFile the input file
     * @param outputFile the output file
     * @param dbName the database name
     * @throws FileNotFoundException if the input file is not found
     * @throws MalformedURLException if the input file uri is incorrect
     */
    public StatementDbConverter(File inputFile, File outputFile, String dbName) throws FileNotFoundException, MalformedURLException {
        super(inputFile, outputFile, dbName);
    }

    /**
     * C'tor
     * 
     * @param inputFile the input url
     * @param outputFile the output file
     * @param dbName the database name
     */
    public StatementDbConverter(URL inputFile, File outputFile, String dbName) {
        super(inputFile, outputFile, dbName);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String convertSingleStatement(String statement) {
        String convertedSt = this.convertStatement(statement);
        if (convertedSt != null && convertedSt.length() > 0) {
            return convertedSt.trim() + this.getNewStatementDelimiterChar();
        } else {
            return null;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void doConvert() throws CouldNotConvertException {
        try {
            final PrintWriter out = new PrintWriter(new FileOutputStream(this.outputFile));
            try {
                out.println(this.getHeader());
                new StatementSplitter(this.inputFile, this.getOldStatementDelimiterChar()) {
                    @Override
                    protected void processStatement(String st) throws CouldNotProcessStatementException {
                        String convertedSt = StatementDbConverter.this.convertStatement(this.cleanUpStatement(st));
                        LOG.debug("ORIG: \n" + st + "\nCONV: \n" + convertedSt);
                        if (convertedSt != null && convertedSt.length() > 0) {
                            out.println(convertedSt.trim() + StatementDbConverter.this.getNewStatementDelimiterChar());
                        }
                    }

                    @Override
                    protected boolean isEndOfStatement(String line) {
                        return StatementDbConverter.this.gotDelimiter(line);
                    }
                }.split();
                out.println(this.getFooter());
            } catch (IOException e) {
                throw new CouldNotConvertException(UtilsMessages.IOEXCEPTION.create(), e);
            } catch (CouldNotProcessStatementException e) {
                throw new CouldNotConvertException(UtilsMessages.CONVERT_PROCESS_FAILED.create(e.getLocalizedMessageHolder()), e);
            } finally {
                out.close();
            }
        } catch (IOException e) {
            throw new CouldNotConvertException(UtilsMessages.IOEXCEPTION.create(), e);
        }
    }

    /**
     * @param line the line being read
     * @return true if the line end with the delimiter char, false otherwise
     */
    protected boolean gotDelimiter(String line) {
        return line.matches(".*" + this.getOldStatementDelimiterChar() + "$");
    }

    /**
     * @return the expected statement delimiter char of the input file
     */
    protected abstract char getOldStatementDelimiterChar();

    /**
     * @return the statement delimiter char to be used in the output file
     */
    protected abstract char getNewStatementDelimiterChar();

    /**
     * @param statement a full statement to be converted
     * @return the converted statement
     */
    protected abstract String convertStatement(String statement);

    /**
     * @return a header to be included in the output file
     */
    protected String getHeader() {
        return "";
    }

    /**
     * @return a footer to be included in the output file
     */
    protected String getFooter() {
        return "";
    }
}